package com.shijunhao.graduation.service.scheduled;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.shijunhao.graduation.domain.bean.HotInfoKey;
import com.shijunhao.graduation.domain.entity.BusinessHotInfo;
import com.shijunhao.graduation.service.business.IBusinessHotInfoService;
import com.shijunhao.graduation.utils.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * @author: shijunhao
 * @date: 2022/4/24 21:11
 * @description: 热度信息的缓存定时任务
 */
@Service
@DependsOn("dataSource")
public class HotInfoScheduledService {

    @Resource
    private IBusinessHotInfoService businessHotInfoService;

    private Logger logger = LoggerFactory.getLogger(HotInfoScheduledService.class);

    private Map<HotInfoKey, BusinessHotInfo> hotInfoCache = new ConcurrentHashMap<>();

    private Map<Long, BusinessHotInfo> hotInfoCache_1 = new ConcurrentHashMap<>();

    private final static String HOT_INFO_PRE_STR = "business:hotInfo:";

    /**
     * @return: void
     * @author: shijunhao
     * @date: 2022/4/24 21:26
     * @description: 每日定时更新进数据库
     */
    //cron = “秒 时 分 日 月 周几”；用空格分隔。每日整点触发
    @Scheduled(cron = "0 0 0 * * ?")
    @PreDestroy
    public void updateInfo(){
        //更新缓存
        Map<HotInfoKey, BusinessHotInfo> newCache = new ConcurrentHashMap<>();
        Map<HotInfoKey, BusinessHotInfo> now = hotInfoCache;
        hotInfoCache = newCache;
        //缓存写入数据库
        boolean b = businessHotInfoService.updateByIds(now.values());
        if(b){
            logger.info("每日热度信息更新成功。。。");
        }else {
            logger.error("每日热度信息更新失败！！！");
        }
    }


    /**
     * @param tableCode: 表值
     * @param nid: id
     * @return: com.shijunhao.graduation.domain.entity.BusinessHotInfo
     * @author: shijunhao
     * @date: 2022/4/24 21:52
     * @description: 获取热度信息
     */
    public BusinessHotInfo getHotInfo(Integer tableCode,Long nid){
        HotInfoKey hotInfoKey = new HotInfoKey(tableCode, nid);
        String redisKey = HOT_INFO_PRE_STR+hotInfoKey.toString();
        //如果有这个值直接返回，没有说明该访问不为恶意访问
        if(RedisUtils.hasKey(redisKey))
            return RedisUtils.get(redisKey,BusinessHotInfo.class);
        //本地没有找到就去查
        if(this.hotInfoCache.get(hotInfoKey) == null){
            BusinessHotInfo hotInfo = businessHotInfoService.getOne(Wrappers.lambdaQuery(BusinessHotInfo.class)
                    .eq(BusinessHotInfo::getTableName, tableCode)
                    .eq(BusinessHotInfo::getNid, nid));
            //本地找的到直接放入返回
            if(hotInfo != null){
                this.hotInfoCache.put(hotInfoKey,hotInfo);
                this.hotInfoCache_1.put(hotInfo.getId(),hotInfo);
                //找不到就放入空置加短时过期,放入缓存防止恶意缓存穿透（查一个不存在的数据）
            }else{
                RedisUtils.set(redisKey, null,3000);
            }
            return hotInfo;
        }
        return this.hotInfoCache.get(hotInfoKey);
    }

    public BusinessHotInfo getHotInfo(Long id){
        String redisKey = HOT_INFO_PRE_STR+id;
        //如果有这个值直接返回，没有说明该访问不为恶意访问
        if(RedisUtils.hasKey(redisKey))
            return RedisUtils.get(redisKey,BusinessHotInfo.class);
        //本地没有找到就去查
        if(this.hotInfoCache_1.get(id) == null){
            BusinessHotInfo hotInfo = businessHotInfoService.getById(id);
            //本地找的到直接放入返回
            if(hotInfo != null){
                this.hotInfoCache_1.put(id,hotInfo);
                this.hotInfoCache.put(new HotInfoKey(hotInfo.getTableName(),hotInfo.getNid()),hotInfo);
                //找不到就放入空置加短时过期,放入缓存防止恶意缓存穿透（查一个不存在的数据）
            }else{
                RedisUtils.set(redisKey, null,3000);
            }
            return hotInfo;
        }
        return this.hotInfoCache.get(id);
    }


    /**
     * @param func: 更新操作
     * @return: void
     * @author: shijunhao
     * @date: 2022/4/24 21:49
     * @description: 更新热度信息
     */
    public void updateHotInfo(Consumer<BusinessHotInfo> func,Integer tableCode,Long nid){
        BusinessHotInfo localData;
        //加1加锁，防止同步操作
        synchronized (this){
            localData = getHotInfo(tableCode,nid);
            if(localData == null)
                return;
            //执行数据变更操作
            func.accept(localData);
        }
        //更新本地信息
        HotInfoKey hotInfoKey = new HotInfoKey(tableCode, nid);
        this.hotInfoCache.put(hotInfoKey,localData);
    }
}
